﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Diagnostics;
using ThreeByte.ActiveDeck.Config;
using ThreeByte.ActiveDeck.Service;

namespace ThreeByte.ActiveDeck.Data.UserResponse
{
    [UserData(ShortTypeName = "Categories")]
    public class CategoriesUserData : AggregateUserData
    {
        
        public List<CategoryData> Categories { get; private set; }
        private Dictionary<int, CategoryData> CategoryLookup = new Dictionary<int, CategoryData>();

        private CategoriesUserData() {
            Categories = new List<CategoryData>();
        }

        protected CategoriesUserData(XElement node) : this() {

            Page = int.Parse(node.Attribute("Page").Value);
            TotalResponses = int.Parse(node.Attribute("TotalResponses").Value);

            foreach(XElement c in node.Elements(ns + "Category")) {
                Categories.Add(CategoryData.ParseXml(c));
            }
        }

        public CategoriesUserData(int page) : this() {
            Page = page;
        }

        public override XElement ToXml() {
            XElement node = base.ToXml();
            Refresh();

            foreach(CategoryData s in Categories) {
                node.Add(s.ToXml());
            }
            return node;
        }

        public void AddResponse(int item, int category) {
            if(!CategoryLookup.ContainsKey(category)) {
                CategoryLookup[category] = new CategoryData(category);
                Categories.Add(CategoryLookup[category]);
            }

            CategoryLookup[category].AddResponse(item);
            
            Refresh();
        }

        public void AddCategory(int category) {
            if(!CategoryLookup.ContainsKey(category)) {
                CategoryLookup[category] = new CategoryData(category);
                Categories.Add(CategoryLookup[category]);
            }
        }

        internal void IncrementSubmissions() {
            TotalResponses++;
        }

        private void Refresh() {
            foreach(CategoryData c in Categories) {
                foreach(CategoryItemValue i in c.ItemValues){
                    i.Percent = (int)(i.Responses * 100.0 / (double)TotalResponses);
                }
            }
        }

        //Update the Answer values and return true if anything changed
        public override bool UpdateUserData(AggregateUserData newUserData) {
            bool modified = false;

            List<CategoryData> unincludedCategories = Categories.ToList(); //Make a copy

            if(!(newUserData is EmptyUserData)) {
                CategoriesUserData newData = (CategoriesUserData)newUserData;

                if(TotalResponses != newData.TotalResponses) {
                    modified = true;
                    TotalResponses = newData.TotalResponses;
                }

                foreach(CategoryData cat in newData.Categories) {
                    CategoryData thisCategory = CategoryLookup[cat.ID];

                    if(thisCategory.UpdateData(cat)) {
                        modified = true;
                    }
                    unincludedCategories.Remove(thisCategory);
                }
            }

            foreach(CategoryData cat in unincludedCategories) {
                if(cat.ItemValues.Count > 0) {
                    cat.ItemValues.Clear();
                    modified = true;
                }
            }

            Refresh();
            return modified;
        }

        public static new CategoriesUserData ParseXml(XElement node) {
            return new CategoriesUserData(node);
        }
    }

    public class CategoryData
    {
        private static readonly XNamespace ns = ServiceConfiguration.NAMESPACE;

        public int ID { get; private set; }

        public List<CategoryItemValue> ItemValues { get; private set; }
        private Dictionary<int, CategoryItemValue> ItemLookup = new Dictionary<int, CategoryItemValue>();

        public CategoryData(int id) {
            ID = id;
            ItemValues = new List<CategoryItemValue>();
        }

        public XElement ToXml() {
            

            XElement node = new XElement(ns + "Category", new XAttribute("ID", ID));

            foreach(CategoryItemValue i in ItemValues.OrderByDescending(v => v.Percent)) {
                node.Add(i.ToXml());
            }
            return node;
        }

        public static CategoryData ParseXml(XElement node) {
            CategoryData newCategory = new CategoryData(int.Parse(node.Attribute("ID").Value));
            foreach(XElement i in node.Elements(ns + "Item")) {
                newCategory.ItemValues.Add(CategoryItemValue.ParseXml(i));
            }
            return newCategory;
        }


        internal void AddResponse(int item) {
            if(!ItemLookup.ContainsKey(item)) {
                ItemLookup[item] = new CategoryItemValue(item);
                ItemValues.Add(ItemLookup[item]);
            }
            ItemLookup[item].Responses++;
        }

        internal bool UpdateData(CategoryData newData) {
            bool modified = false;
            
            
            //These are order sensitive
            if(newData.ItemValues.Count != this.ItemValues.Count) {
                modified = true;
            } else {
                for(int i = 0; i < newData.ItemValues.Count && i < this.ItemValues.Count; i++) {
                    if((newData.ItemValues[i].ID != this.ItemValues[i].ID)
                        || (newData.ItemValues[i].Percent != this.ItemValues[i].Percent)
                        || (newData.ItemValues[i].Responses != this.ItemValues[i].Responses)) {
                        modified = true;
                    }
                }
            }

            if(modified) {
                //Wipe everything out and replace
                this.ItemValues.Clear();
                foreach(CategoryItemValue itemValue in newData.ItemValues) {
                    this.ItemValues.Add(itemValue);
                }
            }

            return modified;
        }
    }

    public class CategoryItemValue
    {
        private static readonly XNamespace ns = ServiceConfiguration.NAMESPACE;

        public int ID { get; private set; }
        public int Responses { get; set; }
        public int Percent { get; set; }

        public CategoryItemValue(int id) {
            ID = id;
        }

        public XElement ToXml() {
            XElement node = new XElement(ns + "Item", new XAttribute("ID", ID),
                                                    new XAttribute("Responses", Responses),
                                                    new XAttribute("Percent", Percent));
            return node;
        }

        public static CategoryItemValue ParseXml(XElement node) {
            return new CategoryItemValue(int.Parse(node.Attribute("ID").Value))
            {
                Responses = int.Parse(node.Attribute("Responses").Value),
                Percent = int.Parse(node.Attribute("Percent").Value)
            };
        }
    }
}
